package com.inspection.service.impl;

import com.google.common.collect.Lists;
import com.inspection.constant.EventApproveStatusEnum;
import com.inspection.constant.YesNoEnum;
import com.inspection.dao.InspectionBasicDao;
import com.inspection.dao.InspectionBranchDao;
import com.inspection.dao.InspectionDepartmentDao;
import com.inspection.dao.InspectionDeptRegulationDao;
import com.inspection.dao.InspectionEventDao;
import com.inspection.dao.InspectionEventDeptRelationDao;
import com.inspection.dao.InspectionRegulationDao;
import com.inspection.entity.InspectionBasicDO;
import com.inspection.entity.InspectionBranchDO;
import com.inspection.entity.InspectionDepartmentDO;
import com.inspection.entity.InspectionDeptRegulationDO;
import com.inspection.entity.InspectionEventDO;
import com.inspection.entity.InspectionEventDeptRelationDO;
import com.inspection.entity.InspectionRegulationDO;
import com.inspection.model.dto.BranchQueryDTO;
import com.inspection.model.dto.InspectionDepartmentQueryDTO;
import com.inspection.model.dto.InspectionStatisticsQueryDTO;
import com.inspection.model.excel.InspectionStaticsExcelVO;
import com.inspection.model.vo.InspectionStaticsVO;
import com.inspection.service.InspectionBasicService;
import com.inspection.service.InspectionBranchService;
import com.inspection.service.InspectionDepartmentService;
import com.inspection.service.InspectionStatisticsService;
import com.inspection.support.AssertUtils;
import com.inspection.support.ExcelUtils;
import com.inspection.support.model.KeyValuePair;
import org.hibernate.type.DateType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zhaoqi
 * @since 2020-11-12
 */
@Service
public class InspectionStatisticsServiceImpl  implements InspectionStatisticsService {


    @Autowired
    private InspectionEventDao inspectionEventDao;

    @Autowired
    private InspectionBasicDao inspectionBasicDao;
    @Autowired
    private InspectionEventDeptRelationDao inspectionEventDeptRelationDao;
    @Autowired
    private InspectionBranchService inspectionBranchService;
    @Autowired
    private InspectionDepartmentService inspectionDepartmentService;




    @Override
    public List<InspectionStaticsVO> queryStatistics(InspectionStatisticsQueryDTO query) throws Exception{
        //验证参数
        query.validate();
        List<InspectionStaticsVO> staticsVOS = new ArrayList<>();
        Date startDate = getDate(query,false);
        Date endDate = getDate(query,true);

        //根据考核类型查
        InspectionBasicDO basicDO = new InspectionBasicDO();
        basicDO.setInspectionCycle(query.getInspectionCycle());
        basicDO.setInspectionType(query.getInspectionType());
        basicDO.setIsDel(0);
        Optional<InspectionBasicDO> one = inspectionBasicDao.findOne(Example.of(basicDO));
        if(!one.isPresent()){
            throw new RuntimeException("该考核类别无此周期或无此考核类别！");
        }
        Specification<InspectionEventDO> specification = (Specification<InspectionEventDO>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            predicates.add(criteriaBuilder.equal(root.get("inspectionType"), query.getInspectionType()));
            predicates.add(criteriaBuilder.equal(root.get("approveStatus"), EventApproveStatusEnum.APPROVE_STATUS.getStatus()));
            predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("eventInspectDate"),startDate));
            predicates.add(criteriaBuilder.lessThan(root.get("eventInspectDate"),endDate));

            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<InspectionEventDO> eventDOS = inspectionEventDao.findAll(specification);
        if(CollectionUtils.isEmpty(eventDOS)){
            return staticsVOS;
        }
        //过滤条例
        if(Objects.nonNull(query.getRegulationId())){
            eventDOS =  eventDOS.stream().filter(e -> e.getRegulationId().equals(query.getRegulationId())).collect(Collectors.toList());
        }
        if(CollectionUtils.isEmpty(eventDOS)){
            return staticsVOS;
        }
        List<InspectionBranchDO> inspectionBranchList = inspectionBranchService.getInspectionBranchList(new BranchQueryDTO());
        Map<Long, InspectionBranchDO> branchDOMap = inspectionBranchList.stream().collect(Collectors.toMap(InspectionBranchDO::getId, InspectionBranchDO -> InspectionBranchDO));
        if(1 == query.getType()){
            Map<InspectionBranchDO,List<InspectionEventDO>> map = new HashMap<>();
            for (InspectionBranchDO inspectionBranchDO : inspectionBranchList) {
                List<InspectionEventDO> collect = eventDOS.stream().filter(e -> e.getInspectBranch().equals(inspectionBranchDO.getId())).collect(Collectors.toList());
                if(!CollectionUtils.isEmpty(collect)){
                    map.put(inspectionBranchDO,collect);
                }
            }
            boolean flag = false;
            for (InspectionBranchDO inspectionBranchDO : inspectionBranchList) {
                for (Map.Entry<InspectionBranchDO, List<InspectionEventDO>> longListEntry : map.entrySet()) {
                    if(inspectionBranchDO.getId().equals(longListEntry.getKey().getId())){
                        InspectionStaticsVO staticsVO = new InspectionStaticsVO();
                        staticsVO.setEventDOList(longListEntry.getValue());
                        staticsVO.setInspectionBranchName(longListEntry.getKey().getBranchName());
                        staticsVO.setInspectionCycle(query.getInspectionCycle());
                        staticsVO.setInspectionTypeName(query.getInspectionType());
                        int sum = longListEntry.getValue().stream().mapToInt(InspectionEventDO::getInspectScore).sum();
                        staticsVO.setInspectionScore(one.get().getInspectionCycleTotalScore()+sum);
                        List<String> inspectionDeptNames = new ArrayList<>();
                        staticsVO.setInspectionDeptNames(inspectionDeptNames);
                        staticsVOS.add(staticsVO);
                        flag = true;
                    }
                }
                if(!flag){
                    InspectionStaticsVO staticsVO = new InspectionStaticsVO();
                    staticsVO.setEventDOList(new ArrayList<InspectionEventDO>());
                    staticsVO.setInspectionBranchName(inspectionBranchDO.getBranchName());
                    staticsVO.setInspectionCycle(query.getInspectionCycle());
                    staticsVO.setInspectionTypeName(query.getInspectionType());
                    staticsVO.setInspectionScore(one.get().getInspectionCycleTotalScore());
                    List<String> inspectionDeptNames = new ArrayList<>();
                    staticsVO.setInspectionDeptNames(inspectionDeptNames);
                    staticsVOS.add(staticsVO);
                }
                flag = false;
            }

        }else{
            InspectionDepartmentQueryDTO deptQuery = new InspectionDepartmentQueryDTO();
            deptQuery.setIsLeafNode(YesNoEnum.YES.getVal());
            List<InspectionDepartmentDO> inspectionDepartmentDOS = inspectionDepartmentService.queryDepartment(deptQuery);
            Map<Long, InspectionDepartmentDO> deptMap = inspectionDepartmentDOS.stream().collect(Collectors.toMap(InspectionDepartmentDO::getId, InspectionDepartmentDO -> InspectionDepartmentDO));
            List<Long> eventIdList = eventDOS.stream().map(InspectionEventDO::getId).collect(Collectors.toList());
            List<InspectionEventDeptRelationDO> relationDOList = inspectionEventDeptRelationDao.findByInspectionEventIdIn(eventIdList);
            List<Long> collect = relationDOList.stream().map(InspectionEventDeptRelationDO::getInspectionDeptId).distinct().collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(collect)){
                for (Long aLong : collect) {
                    List<Long> collect1 = relationDOList.stream().filter(relation -> relation.getInspectionDeptId().equals(aLong)).map(InspectionEventDeptRelationDO::getInspectionEventId).collect(Collectors.toList());
                    List<InspectionEventDO> collect2 = eventDOS.stream().filter(event -> collect1.contains(event.getId())).collect(Collectors.toList());
                    InspectionStaticsVO staticsVO = new InspectionStaticsVO();
                    staticsVO.setEventDOList(collect2);
                    InspectionDepartmentDO inspectionDepartmentDO = deptMap.get(aLong);
                    if(inspectionDepartmentDO.getBranchId() != null){
                        staticsVO.setInspectionBranchName(branchDOMap.get(inspectionDepartmentDO.getBranchId()).getBranchName());
                    }
                    staticsVO.setInspectionCycle(query.getInspectionCycle());
                    staticsVO.setInspectionTypeName(query.getInspectionType());
                    int sum = collect2.stream().mapToInt(InspectionEventDO::getInspectScore).sum();
                    staticsVO.setInspectionScore(one.get().getInspectionCycleTotalScore()+sum);
                    List<String> inspectionDeptNames = new ArrayList<>();
                    inspectionDeptNames.add(inspectionDepartmentDO.getDeptName());
                    staticsVO.setInspectionDeptNames(inspectionDeptNames);
                    staticsVOS.add(staticsVO);
                }
            }
        }
        Collections.sort(staticsVOS, Comparator.comparingInt(InspectionStaticsVO::getInspectionScore));
        return staticsVOS;
    }

    @Override
    public void exportStatistics(InspectionStatisticsQueryDTO query, HttpServletResponse response) {

        try {
            List<InspectionStaticsVO> vos = queryStatistics(query);

            List<InspectionStaticsExcelVO> excelVOS=new ArrayList<>(vos.size());

            for (InspectionStaticsVO statics : vos) {
                InspectionStaticsExcelVO vo = new InspectionStaticsExcelVO();
                BeanUtils.copyProperties(statics, vo);
                vo.setInspectDeptNames(String.join(",", statics.getInspectionDeptNames()));
                excelVOS.add(vo);
            }


            ArrayList<KeyValuePair> pairs = new ArrayList<>();
            pairs.add(KeyValuePair.builder().key("inspectionBranchName").value("被考核支部").build());
            pairs.add(KeyValuePair.builder().key("inspectDeptNames").value("被考核部门").build());
            
            pairs.add(KeyValuePair.builder().key("inspectionTypeName").value("考核类型").build());
            pairs.add(KeyValuePair.builder().key("inspectionCycle").value("考核周期").build());

            pairs.add(KeyValuePair.builder().key("inspectionScore").value("考核分数").build());



            ExcelUtils.export(this.buildStatisticsTitle(query), excelVOS, pairs, response);



        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String buildStatisticsTitle(InspectionStatisticsQueryDTO query) {
        //年    XXXX年
        //半年  XXXX年 上半年/下半年
        //季度  XXXX年 第X季度
        //月    XXXX年 第X月

        switch (query.getInspectionCycle()) {
            case "年":
                return String.format("%s年 考核排名", query.getYear());
            case "半年":
                return String.format("%s年%s 考核排名", query.getYear(), query.getInspectionCycleNumber() == 1 ? "上半年" : "下半年");
            case "季":
                return String.format("%s年第%s季度 考核排名", query.getYear(), query.getInspectionCycleNumber());
            case "月":
                return String.format("%s年第%s月 考核排名", query.getYear(), query.getInspectionCycleNumber());
            default:
                return "";
        }
       

    }

    private Date getDate(InspectionStatisticsQueryDTO query,boolean end) throws Exception{
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR,query.getYear());
        calendar.set(Calendar.DAY_OF_MONTH,1);
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        calendar.set(Calendar.MILLISECOND,0);

        switch (query.getInspectionCycle()) {
            case "月" :
                if(query.getInspectionCycleNumber()>12 || query.getInspectionCycleNumber() <1){
                    throw new RuntimeException("传入考核周期非法！");
                }
                calendar.set(Calendar.MONTH,query.getInspectionCycleNumber()-1);
                if(end){
                    calendar.add(Calendar.MONTH,1);
                }
                break;
            case "季" :
                if(query.getInspectionCycleNumber()>4 || query.getInspectionCycleNumber() <1){
                    throw new RuntimeException("传入考核周期非法！");
                }
                calendar.set(Calendar.MONTH,(query.getInspectionCycleNumber()-1)*3);
                if(end){
                    calendar.add(Calendar.MONTH,3);
                }
                break;
            case "半年" :
                if(query.getInspectionCycleNumber()>2 || query.getInspectionCycleNumber() <1){
                    throw new RuntimeException("传入考核周期非法！");
                }
                calendar.set(Calendar.MONTH,(query.getInspectionCycleNumber()-1)*6);
                if(end){
                    calendar.add(Calendar.MONTH,6);
                }
                break;
            case "年" :
                calendar.set(Calendar.MONTH,0);
                if(end){
                    calendar.add(Calendar.MONTH,12);
                }
                break;
            default:
                throw new RuntimeException("传入考核周期非法！");
        }
        return calendar.getTime();
    }

}
